﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleAlgorithms.Utils
{
    /// <summary>
    /// 用于表示分数的类。
    /// </summary>
    public class Fraction : ITestable
    {

        // 用法展示
        public void Test()
        {
            var f1 = new Fraction(1, 2);
            var f2 = new Fraction(-1, 24);
            var f3 = new Fraction(-10, 12);
            var f4 = new Fraction(-1, 24);

            Console.WriteLine($"初始化:\tf1 = {f1}");
            Console.WriteLine($"浮点化:\tf1 = {f1.toDouble()}");

            var f11 = f1 + f2;
            Console.WriteLine($"加法1:\t{f1} + {f2} = {f11}");

            var f12 = f11 + f3;
            Console.WriteLine($"加法2:\t{f11} + {f3} = {f12}");

            var f13 = f12 - f4;
            Console.WriteLine($"减法:\t{f12} - {f4} = {f13}");

            var f14 = f13 * f2;
            Console.WriteLine($"乘法:\t{f13} * {f2} = {f14}");

            var f15 = f14 / f4;
            Console.WriteLine($"除法:\t {f14} / {f4} = {f15}");
        }


        public static void Test1()
        {
            var f1 = new Fraction(1, 6);
            var f2 = new Fraction(1, 9);
            var f3 = new Fraction(1, 18);
            Console.WriteLine(f1 + f2 + f3);
        }


        public int sign;           // 符号
        public int numerator;      // 分子
        public int denominator;    // 分母


        public Fraction() { }

        /**
         * Constructs a fraction object from a numerator and a denominator.
         * @param numerator The numerator.
         * @param denominator The denominator.
         */
        public Fraction(int numerator, int denominator)
        {
            if (denominator == 0)
            {
                try
                {
                    throw new Exception("Denominator cannot be zero.");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            this.sign = 1;
            this.numerator = numerator;
            this.denominator = denominator;
            simplify();
        }

        public Fraction(int sign, int numerator, int denominator)
        {
            if (denominator == 0)
            {
                try
                {
                    throw new Exception("Denominator cannot be zero.");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            this.sign = sign;
            this.numerator = numerator;
            this.denominator = denominator;
            simplify();
        }

        /**
         * Simplify a fraction such as 2/4 -> 1/2.
         */
        public void simplify()
        {
            if (numerator == 0)
                return;
            sign = sign == 0 ? 1 : sign;
            sign = sign * numerator * denominator > 0 ? 1 : -1;
            numerator = Math.Abs(numerator);
            denominator = Math.Abs(denominator);

            int gcd = getGreatestCommonDivider(numerator, denominator);
            numerator /= gcd;
            denominator /= gcd;
        }

        /**
         * To get the greatest common divider of n and m.
         * @param n the first argument to calculate the divider.
         * @param m the second argument to calculate the divider.
         * @return
         */
        public int getGreatestCommonDivider(int n, int m)
        {
            int a = Math.Abs(n);
            int b = Math.Abs(m);

            int gcd = (a < b ? a : b);
            for (; gcd >= 1; gcd--)
                if (a % gcd == 0 && b % gcd == 0)
                    break;

            return gcd;
        }

        /***********************************************
         *     加减乘除四则运算和相应的四个方便操作的重载。
         ***********************************************/

        public Fraction add(Fraction fraction)
        {
            numerator = this.sign * (numerator * fraction.denominator) + (fraction.numerator * denominator) * fraction.sign;
            denominator = denominator * fraction.denominator;
            simplify();
            return this;
        }


        public Fraction add(int n, int d)
        {
            return add(new Fraction(n, d));
        }


        public Fraction sub(Fraction fraction)
        {
            return add(-fraction.numerator, fraction.denominator);
        }

        public Fraction sub(int n, int m)
        {
            return add(-n, m);
        }

        public Fraction multiply(Fraction fraction)
        {
            this.sign *= fraction.sign;
            this.numerator *= fraction.numerator;
            this.denominator *= fraction.denominator;
            this.simplify();
            return this;
        }


        public Fraction multiply(int n, int d)
        {
            return multiply(new Fraction(n, d));
        }

        public Fraction divide(Fraction fraction)
        {
            return multiply(fraction.sign * fraction.denominator, fraction.numerator);
        }

        public Fraction divide(int n, int d)
        {
            return divide(new Fraction(n, d));
        }

        /**
         * Converts to a floating number of Double.
         * @return
         */
        public double toDouble()
        {
            return 1.0 * sign * numerator / denominator;
        }

        public static Fraction operator +(Fraction f1, Fraction f2)
        {
            Fraction f3 = new Fraction();
            f3.numerator = f1.sign * (f1.numerator * f2.denominator) + f2.sign * (f2.numerator * f1.denominator);
            f3.denominator = f1.denominator * f2.denominator;
            f3.simplify();
            return f3;
        }

        public static Fraction operator -(Fraction f1, Fraction f2)
        {
            Fraction f3 = new Fraction();
            f3.numerator = f1.sign * (f1.numerator * f2.denominator) - f2.sign * (f2.numerator * f1.denominator);
            f3.denominator = f1.denominator * f2.denominator;
            f3.simplify();
            return f3;
        }

        public static Fraction operator *(Fraction f1, Fraction f2)
        {
            var f3 = f1.Clone();
            f3.sign *= f2.sign;
            f3.numerator *= f2.numerator;
            f3.denominator *= f2.denominator;
            f3.simplify();
            return f3;
        }

        public static Fraction operator /(Fraction f1, Fraction f2)
        {
            return f1 * new Fraction(f2.sign, f2.denominator, f2.numerator);
        }

        public Fraction Clone()
        {
            return new Fraction(this.sign, this.numerator, this.denominator);
        }


        public override string ToString()
        {
            return (sign == 1 ? "" : "-") + numerator + "/" + denominator;
        }
    }



}
